package com.imyuanma.qingyun.lowcode.service.impl;

import com.imyuanma.qingyun.common.exception.Exceptions;
import com.imyuanma.qingyun.common.model.PageQuery;
import com.imyuanma.qingyun.common.util.AssertUtil;
import com.imyuanma.qingyun.common.util.CollectionUtil;
import com.imyuanma.qingyun.common.util.DateUtil;
import com.imyuanma.qingyun.common.util.StringUtil;
import com.imyuanma.qingyun.lowcode.dao.ILcpGenerateFieldDao;
import com.imyuanma.qingyun.lowcode.dao.ILcpGenerateMainDao;
import com.imyuanma.qingyun.lowcode.factory.GenerateBOFactory;
import com.imyuanma.qingyun.lowcode.factory.GenerateDOFactory;
import com.imyuanma.qingyun.lowcode.model.bo.GenerateFieldBO;
import com.imyuanma.qingyun.lowcode.model.bo.GenerateMainBO;
import com.imyuanma.qingyun.lowcode.model.data.*;
import com.imyuanma.qingyun.lowcode.model.enums.ELcpFormControlTypeEnum;
import com.imyuanma.qingyun.lowcode.model.enums.ELcpFormItemDataSourceTypeEnum;
import com.imyuanma.qingyun.lowcode.model.enums.ELcpQueryConditionTypeEnum;
import com.imyuanma.qingyun.lowcode.service.ILcpDbService;
import com.imyuanma.qingyun.lowcode.service.ILcpGenerateService;
import com.imyuanma.qingyun.lowcode.service.ILcpTemplateGroupService;
import com.imyuanma.qingyun.lowcode.util.GenerateSerializeUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 代码生成服务
 *
 * @author wangjy
 * @date 2022/05/04 14:30:46
 */
@Transactional(rollbackFor = Throwable.class)
@Service
@Slf4j
public class LcpGenerateServiceImpl implements ILcpGenerateService {

    /**
     * 数据库服务
     */
    @Autowired
    private ILcpDbService dbService;
    /**
     * 代码生成dao
     */
    @Autowired
    private ILcpGenerateMainDao generateMainDao;
    /**
     * 代码生成字段配置dao
     */
    @Autowired
    private ILcpGenerateFieldDao generateFieldDao;
    /**
     * 代码模板
     */
    @Autowired
    private ILcpTemplateGroupService templateGroupService;

    /**
     * 生成代码
     *
     * @param generateId     代码生成配置id
     * @param codeTemplateId 代码模板id
     */
    @Override
    public void generate(Long generateId, Long codeTemplateId) {
        // 代码生成配置详情
        GenerateMainBO generateMainBO = this.getGenerateDetail(generateId);
        // 字段排序
        generateMainBO.getFieldList().sort((o1, o2) -> {
            int i1 = o1.getListColumnOrder() != null ? o1.getListColumnOrder() : 0;
            int i2 = o2.getListColumnOrder() != null ? o2.getListColumnOrder() : 0;
            return i1 - i2;
        });

        // 根目录
        String rootPath = generateMainBO.getSavePath();
        // 将配置信息放入map,在ftl中使用
        Map<String, Object> map = GenerateSerializeUtil.buildCodeTemplateParam(generateMainBO);
        // 首字母小写的类名
        String classNameLowerFirst = String.valueOf(generateMainBO.getClassName().charAt(0)).toLowerCase() + generateMainBO.getClassName().substring(1);
        // 包路径目录格式
        String packagePathDir = generateMainBO.getPackagePath().replace(".", "/");

        // 查询模板
        if (codeTemplateId != null) {
            // 查询模板
            LcpTemplateGroup templateGroup = templateGroupService.detail(codeTemplateId);
            if (templateGroup != null && CollectionUtil.isNotEmpty(templateGroup.getTemplateItemList())) {
                for (LcpTemplateItem templateItem : templateGroup.getTemplateItemList()) {
                    if (StringUtil.isBlank(templateItem.getTemplateContent())) {
                        log.warn("[生成代码] 代码模板的模板内容为空,跳过,id={},名称={},模板组名称={}", templateItem.getId(), templateItem.getName(), templateGroup.getName());
                        continue;
                    }
                    // 生成代码目录解析
                    String codeFilePath = rootPath + templateItem.getFilePath().replaceAll("\\{packagePath}", packagePathDir)
                            .replaceAll("\\{moduleName}", generateMainBO.getModuleName())
                            .replaceAll("\\{className}", generateMainBO.getClassName())
                            .replaceAll("\\{className_lower}", classNameLowerFirst);
                    // 代码生成
                    GenerateSerializeUtil.generateFileByTemplateString(templateItem.getName(), templateItem.getTemplateContent(), codeFilePath, map);
                }
                return;
            }
        }


        // java包目录
        String packagePath = String.format("%s/src/main/java/%s/%s", rootPath, packagePathDir, generateMainBO.getModuleName());
        // 资源目录,mapper文件
        String resourcePath = String.format("%s/src/main/resources/%s/%s", rootPath, packagePathDir, generateMainBO.getModuleName());
        // html目录
        String pagePath = String.format("%s/src/main/resources/page/%s", rootPath, generateMainBO.getModuleName());

        // 各种java对象目录
        String path_model = String.format("%s/model", packagePath);
        String path_dao = String.format("%s/dao", packagePath);
        String path_service = String.format("%s/service", packagePath);
        String path_service_impl = String.format("%s/service/impl", packagePath);
        String path_util = String.format("%s/util", packagePath);
        String path_web = String.format("%s/controller", packagePath);
        String path_mapper = String.format("%s/mapper/mysql", resourcePath);
        String path_html = String.format("%s", pagePath);


        // 模型类
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "Model.ftl", String.format("%s/%s.java", path_model, generateMainBO.getClassName()), map);
        // mapper
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "Mapper.ftl", String.format("%s/%sMapper.xml", path_mapper, generateMainBO.getClassName()), map);
        // dao
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "Dao.ftl", String.format("%s/I%sDao.java", path_dao, generateMainBO.getClassName()), map);
        // service
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "Service.ftl", String.format("%s/I%sService.java", path_service, generateMainBO.getClassName()), map);
        // 服务实现
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "ServiceImpl.ftl", String.format("%s/%sServiceImpl.java", path_service_impl, generateMainBO.getClassName()), map);
        // web
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "Controller.ftl", String.format("%s/%sController.java", path_web, generateMainBO.getClassName()), map);
        // 页面
        GenerateSerializeUtil.generateFile(GenerateSerializeUtil.CODE_TEMPLATE_DIR_PATH, "html_list.ftl", String.format("%s/%sList.html", path_html, classNameLowerFirst), map);

    }



    /**
     * 初始化代码生成配置
     *
     * @param tableName 表名
     * @return
     */
    @Override
    public GenerateMainBO initGenerateByTableName(String tableName) {
        // 查询表信息
        LcpDbTableDO table = dbService.getTableByTableName(tableName);
        AssertUtil.notNull(table, "查询表信息为空");

        // 查询表字段
        List<LcpDbColumnDO> columnList = dbService.getColumnListByTableName(tableName);
        AssertUtil.notEmpty(columnList, "查询表字段为空");

        return GenerateBOFactory.buildGenerateMainBO(table, columnList);
    }

    /**
     * 新增代码生成配置
     *
     * @param generateMainBO 代码生成配置
     */
    @Override
    public void addGenerateConfig(GenerateMainBO generateMainBO) {
        for (GenerateFieldBO fieldBO : generateMainBO.getFieldList()) {
            // 不支持数据源配置的字段, 清空数据源配置
            if (fieldBO.getFormControlDataSource() != null && !ELcpFormControlTypeEnum.isSupportDataSource(fieldBO.getFormControlType())) {
                fieldBO.getFormControlDataSource().setDataType(ELcpFormItemDataSourceTypeEnum.NO.getType());
            }
        }
        // 主配置
        LcpGenerateMainDO generateMainDO = GenerateDOFactory.buildGenerateMainDO(generateMainBO);
        // 插入主配置
        generateMainDao.insert(generateMainDO);
        // 字段配置
        List<LcpGenerateFieldDO> fieldDOList = GenerateDOFactory.buildGenerateFieldDOList(generateMainBO.getFieldList());
        for (LcpGenerateFieldDO generateFieldDO : fieldDOList) {
            generateFieldDO.setGenerateId(generateMainDO.getId());
            generateFieldDao.insert(generateFieldDO);
        }
    }

    /**
     * 更新代码生成配置
     *
     * @param generateMainBO 代码生成配置
     */
    @Override
    public void update(GenerateMainBO generateMainBO) {
        for (GenerateFieldBO fieldBO : generateMainBO.getFieldList()) {
            // 不支持数据源配置的字段, 清空数据源配置
            if (fieldBO.getFormControlDataSource() != null && !ELcpFormControlTypeEnum.isSupportDataSource(fieldBO.getFormControlType())) {
                fieldBO.getFormControlDataSource().setDataType(ELcpFormItemDataSourceTypeEnum.NO.getType());
            }
        }
        // 主配置
        LcpGenerateMainDO generateMainDO = GenerateDOFactory.buildGenerateMainDO(generateMainBO);
        // 插入主配置
        generateMainDao.update(generateMainDO);

        // 字段配置
        List<LcpGenerateFieldDO> fieldDOList = GenerateDOFactory.buildGenerateFieldDOList(generateMainBO.getFieldList());
        for (LcpGenerateFieldDO generateFieldDO : fieldDOList) {
            if (generateFieldDO.getId() == null) {
                generateFieldDO.setGenerateId(generateMainDO.getId());
                generateFieldDao.insert(generateFieldDO);
            } else {
                generateFieldDao.update(generateFieldDO);
            }
        }
    }

    /**
     * 查询代码生成列表
     *
     * @param generateMainBO 代码生成查询条件
     * @return
     */
    @Override
    public List<GenerateMainBO> getList(GenerateMainBO generateMainBO) {
        LcpGenerateMainDO generateMainDO = GenerateDOFactory.buildGenerateMainDO(generateMainBO);
        List<LcpGenerateMainDO> list = generateMainDao.getList(generateMainDO);
        if (list == null) {
            return null;
        }
        return list.stream().map(item -> GenerateBOFactory.buildGenerateMainBO(item, null)).collect(Collectors.toList());
    }

    /**
     * 分页查询
     *
     * @param generateMainBO 代码生成查询条件
     * @param pageQuery      分页参数
     * @return
     */
    @Override
    public List<GenerateMainBO> getPage(GenerateMainBO generateMainBO, PageQuery pageQuery) {
        LcpGenerateMainDO generateMainDO = GenerateDOFactory.buildGenerateMainDO(generateMainBO);
        List<LcpGenerateMainDO> list = generateMainDao.getList(generateMainDO, pageQuery);
        if (list == null) {
            return null;
        }
        return list.stream().map(item -> GenerateBOFactory.buildGenerateMainBO(item, null)).collect(Collectors.toList());
    }

    /**
     * 查询代码生成详情
     *
     * @param id 代码生成id
     * @return
     */
    @Override
    public GenerateMainBO getGenerateDetail(Long id) {
        // 查询主配置
        LcpGenerateMainDO lcpGenerateMainDO = generateMainDao.get(id);
        AssertUtil.notNull(lcpGenerateMainDO, "查询代码生成配置为空");
        // 查询字段配置
        LcpGenerateFieldDO fieldQuery = new LcpGenerateFieldDO();
        fieldQuery.setGenerateId(id);
        List<LcpGenerateFieldDO> fieldDOList = generateFieldDao.getList(fieldQuery);
        AssertUtil.notEmpty(fieldDOList, "查询代码生成字段配置为空");
        return GenerateBOFactory.buildGenerateMainBO(lcpGenerateMainDO, fieldDOList);
    }

    /**
     * 同步字段变更并返回最新详情
     *
     * @param id 代码生成id
     * @return
     */
    @Override
    public GenerateMainBO syncFieldChange(Long id) {
        // 查询详情
        GenerateMainBO generateMainBO = this.getGenerateDetail(id);
        AssertUtil.notNull(generateMainBO, "代码生成配置为空");
        // 原来的字段
        Set<String> colNameSet = generateMainBO.getFieldList().stream().map(GenerateFieldBO::getFieldDbName).collect(Collectors.toSet());
        // 查询变更
        List<LcpDbColumnDO> columnList = dbService.getColumnListByTableName(generateMainBO.getTableName());
        AssertUtil.notEmpty(columnList, "查询表" + generateMainBO.getTableName() + "字段为空");
        // 找到新增的字段
        List<GenerateFieldBO> newFieldList = columnList.stream().filter(item -> !colNameSet.contains(item.getColumnName())).map(GenerateBOFactory::buildGenerateFieldBO).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(newFieldList)) {
            generateMainBO.getFieldList().addAll(newFieldList);
        }
        return generateMainBO;
    }

    /**
     * 删除
     *
     * @param ids 代码生成id
     */
    @Override
    public void delete(Long[] ids) {
        for (Long id : ids) {
            // 删除主配置
            generateMainDao.delete(id);
            // 删除字段配置
            generateFieldDao.deleteByGenerateId(id);
        }
    }
}
